/**
 * Copyright (C) 2008+ Spark Zheng
 *
 * test/test_list.c
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include "list.h"

typedef struct data_head {
	list_node_t	head;
	uint32_t	num;
} S_HEAD;

typedef struct data_node {
	list_node_t	ln;
	int32_t		data;
} S_NODE;

typedef struct data_hlist_head {
	hlist_head_t	head;
	uint32_t	num;
} S_H_HEAD;

typedef struct data_hlist_node {
	hlist_node_t	hn;
	int32_t	data;
} S_H_NODE;
	

int main(int argc, char *argv[])
{
	uint32_t i = 0;
	uint32_t j = 0;
	uint32_t len = 20;
	
	S_HEAD l_head;
	S_H_HEAD h_head;
	
	if (argc < 2) {
		fprintf(stderr,"Usage: test_list " \
			       "[l(double-list test)|h(hlist test)] [len]\n");
		return -1;
	}
	
	if (argc == 3 && atoi(argv[2]) > 12)
		len = (uint32_t)atoi(argv[2]);
	
if (strchr(argv[1], 'l') != NULL) {
	fprintf(stdout, "LIST: test double-linked list begin...\n");
	
	LIST_HEAD_INIT(&l_head.head);
	l_head.num = 0;
	
	S_NODE l_node[len];
	S_NODE *tpos = NULL;
	list_node_t *pos = NULL;
	
	S_NODE b_node[3];
	S_NODE c_node[3];

	for (i = 1; i <= len; i++) {
		l_node[i - 1].data = i;
		LIST_ADD_TAIL(&l_head.head, &l_node[i - 1].ln);
		l_head.num++;
	}

	b_node[0].data = 0;
	b_node[1].data = len / 2;
	b_node[2].data = len + 10;

	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		if (tpos->data >= b_node[0].data) {
			break;
		}
	}
	//if (pos == &l_head.head && tpos == NULL) {
	//	LIST_ADD_HEAD(&l_head.head , &b_node[0].ln);
	if (pos == &l_head.head) {
		LIST_ADD_TAIL(&l_head.head , &b_node[0].ln);
	} else {
		LIST_INSERT_BEFORE(&tpos->ln , &b_node[0].ln);
	}

	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		if (tpos->data >= b_node[1].data) {
			break;
		}
	}
	//if (pos == &l_head.head && tpos == NULL) {
	//	LIST_ADD_HEAD(&l_head.head , &b_node[1].ln);
	if (pos == &l_head.head) {
		LIST_ADD_TAIL(&l_head.head , &b_node[1].ln);
	} else {
		LIST_INSERT_BEFORE(&tpos->ln , &b_node[1].ln);
	}

	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		if (tpos->data >= b_node[2].data) {
			break;
		}
	}
	//if (pos == &l_head.head && tpos == NULL) {
	//	LIST_ADD_HEAD(&l_head.head , &b_node[2].ln);
	if (pos == &l_head.head) {
		LIST_ADD_TAIL(&l_head.head , &b_node[2].ln);
	} else {
		LIST_INSERT_BEFORE(&tpos->ln , &b_node[2].ln);
	}

	c_node[0].data = -1;
	c_node[1].data = len / 2 + 1;
	c_node[2].data = len + 20;

	LIST_FOREACH_ENTRY_PREV(tpos, pos, &l_head.head, ln) {
		if (tpos->data <= c_node[0].data) {
			break;
		}
	}
	//if (pos == &l_head.head && tpos == NULL) {
	//	LIST_ADD_TAIL(&l_head.head , &c_node[0].ln);
	if (pos == &l_head.head) {
		LIST_ADD_HEAD(&l_head.head , &c_node[0].ln);
	} else {
		LIST_INSERT_AFTER(&tpos->ln , &c_node[0].ln);
	}

	LIST_FOREACH_ENTRY_PREV(tpos, pos, &l_head.head, ln) {
		if (tpos->data <= c_node[1].data) {
			break;
		}
	}
	//if (pos == &l_head.head && tpos == NULL) {
	//	LIST_ADD_TAIL(&l_head.head , &c_node[1].ln);
	if (pos == &l_head.head) {
		LIST_ADD_HEAD(&l_head.head , &c_node[1].ln);
	} else {
		LIST_INSERT_AFTER(&tpos->ln , &c_node[1].ln);
	}

	LIST_FOREACH_ENTRY_PREV(tpos, pos, &l_head.head, ln) {
		if (tpos->data <= c_node[2].data) {
			break;
		}
	}
	//if (pos == &l_head.head && tpos == NULL) {
	//	LIST_ADD_TAIL(&l_head.head , &c_node[2].ln);
	if (pos == &l_head.head) {
		LIST_ADD_HEAD(&l_head.head , &c_node[2].ln);
	} else {
		LIST_INSERT_AFTER(&tpos->ln , &c_node[2].ln);
	}
	
	fprintf(stdout, "LIST: the original list is:\n");
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	S_NODE *first = LIST_FIRST_ENTRY(&l_head.head, S_NODE, ln);
	S_NODE *last = LIST_LAST_ENTRY(&l_head.head, S_NODE, ln);
	fprintf(stdout, "LIST: first node: %d\n", first->data);
	fprintf(stdout, "LIST: last node: %d\n", last->data);
	
	S_NODE *next = LIST_NEXT_ENTRY(&l_head.head, &last->ln, S_NODE, ln);
	if (next == NULL) {
		fprintf(stdout, "LIST: next for last node: NULL\n");
	} else {
		fprintf(stdout, "LIST: next for last node: %d\n", next->data);
	}

	i = 5;
	LIST_DEL(&l_node[i].ln);
	fprintf(stdout, "LIST: delete %dth node, list is:\n", i + 1);
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	j = 10;
	LIST_REPLACE(&l_node[j].ln, &l_node[i].ln);
	fprintf(stdout, "LIST: replace %dth node with %dth node, list is:\n",
		j + 1, i + 1);
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	LIST_ADD_HEAD(&l_head.head, &l_node[j].ln);
	fprintf(stdout, "LIST: add %dth node to head, list is:\n", j + 1);
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	i = 5;
	LIST_DEL(&l_node[i].ln);
	LIST_ADD_TAIL(&l_head.head, &l_node[i].ln);
	fprintf(stdout, "LIST: add %dth node to tail, list is:\n", i + 1);
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	i = 5;
	LIST_DEL(&l_node[i].ln);
	LIST_INSERT_BEFORE(&l_node[i+1].ln, &l_node[i].ln);
	fprintf(stdout, "LIST: insert %dth node before %dth node, list is:\n",
		i + 1, i + 2);
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	j = 10;
	LIST_DEL(&l_node[j].ln);
	LIST_INSERT_AFTER(&l_node[j-1].ln, &l_node[j].ln);
	fprintf(stdout, "LIST: insert %dth node after %dth node, list is:\n",
		j + 1, j);
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	/**********************************************************/
	S_NODE ll_node[len];
	ll_node[0].data = 1 + len;
	LIST_HEAD_INIT(&ll_node[0].ln);
	for (i = 2; i <= len; i++) {
		ll_node[i - 1].data = i + len;
		LIST_ADD_TAIL(&ll_node[0].ln, &ll_node[i - 1].ln);
	}
	
	LIST_SPLICE(&ll_node[0].ln, &l_head.head);
	
	fprintf(stdout, "LIST: splice two list, list is:\n");
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	S_NODE lll_node[len];
	lll_node[0].data = 1 + len;
	LIST_HEAD_INIT(&lll_node[0].ln);
	for (i = 2; i <= len; i++) {
		lll_node[i - 1].data = i + len;
		LIST_ADD_TAIL(&lll_node[0].ln, &lll_node[i - 1].ln);
	}
	
	LIST_SPLICE_TAIL(&lll_node[0].ln, &l_head.head);
	fprintf(stdout, "LIST: splice two list, list is:\n");
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY_PREV(tpos, pos, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");

	list_node_t *n = NULL;
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY_SAFE(tpos, pos, n, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	fprintf(stdout, "[HEAD] <=> ");
	LIST_FOREACH_ENTRY_PREV_SAFE(tpos, pos, n, &l_head.head, ln) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	fprintf(stdout, "LIST: test double-linked list finished.\n");
}

/******************************************************************/
if (strchr(argv[1], 'h') != NULL) {
	fprintf(stdout, "LIST: test hlist begin...\n");
	
	HLIST_HEAD_INIT(&h_head.head);
	h_head.num = 0;
	
	S_H_NODE h_node[len];
	S_H_NODE *tpos;
	hlist_node_t *h_pos;
	
	for (i = len; i >= 1; i--) {
		h_node[i - 1].data = i;
		HLIST_ADD(&h_head.head, &h_node[i - 1].hn);
		h_head.num++;
	}
	
	fprintf(stdout, "LIST: the original hlist is:\n");
	fprintf(stdout, "[HEAD] <=> ");
	HLIST_FOREACH_ENTRY(tpos, h_pos, &h_head.head, hn) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	S_H_NODE *first = HLIST_FIRST_ENTRY(&h_head.head, S_H_NODE, hn);
	fprintf(stdout, "LIST: first node: %d\n", first->data);
	S_H_NODE *next = HLIST_NEXT_ENTRY(&h_node[0].hn, S_H_NODE, hn);
	fprintf(stdout, "LIST: next node of first: %d\n", next->data);
	
	HLIST_POP_ENTRY(&h_head.head, hn, first);
	fprintf(stdout, "LIST: first node: %d\n", first->data);
	HLIST_PUSH(&h_head.head, &first->hn);
	
	i = 5;
	HLIST_DEL(&h_node[i].hn);
	fprintf(stdout, "LIST: delete %dth node, hlist is:\n", i + 1);
	fprintf(stdout, "[HEAD] <=> ");
	HLIST_FOREACH_ENTRY(tpos, h_pos, &h_head.head, hn) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	j = 10;
	HLIST_REPLACE(&h_node[j].hn, &h_node[i].hn);
	fprintf(stdout, "LIST: replace %dth node with %dth node, hlist is:\n",
		j + 1, i + 1);
	fprintf(stdout, "[HEAD] <=> ");
	HLIST_FOREACH_ENTRY(tpos, h_pos, &h_head.head, hn) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	
	HLIST_INSERT_AFTER(&h_node[j - 1].hn, &h_node[j].hn);
	fprintf(stdout, "LIST: insert %dth node after %dth node, hlist is:\n",
		j + 1, j);
	fprintf(stdout, "[HEAD] <=> ");
	HLIST_FOREACH_ENTRY(tpos, h_pos, &h_head.head, hn) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	i = 5;
	HLIST_DEL(&h_node[i].hn);
	HLIST_INSERT_BEFORE(&h_node[i+1].hn, &h_node[i].hn);
	fprintf(stdout, "LIST: insert %dth node before %dth node, hlist is:\n",
		i +  1, i);
	fprintf(stdout, "[HEAD] <=> ");
	HLIST_FOREACH_ENTRY(tpos, h_pos, &h_head.head, hn) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	hlist_node_t *h_n;
	fprintf(stdout, "[HEAD] <=> ");
	HLIST_FOREACH_ENTRY_SAFE(tpos, h_pos, h_n, &h_head.head, hn) {
		fprintf(stdout, "%d <=> ", tpos->data); 
	}
	fprintf(stdout, "[HEAD]\n");
	
	fprintf(stdout, "LIST: test hlist finished.\n");
}
	
	return 0;
}
